前端在串接 API 時很常會看到同源政策和 CORS 兩個詞,這篇來複習一下這兩個究竟是何物。
同源政策的全名為 Same Origin Policy。
當我們嘗試修改或讀取其他人的網站,會收到 blocked by CORS policy
的錯誤,因為瀏覽器會禁止跨域請求,這就是所謂的「同源政策」。
而同源政策又分成 DOM 同源政策
和 Cookie 同源政策
兩種類型。
只要 scheme
、domain
、port
三者相同,就會視為同源。
如果以 https://example.com:8000
為例子來看的話:
http://example.com // 不同源,scheme 不同
https://example.org // 不同源,domain 不同
http://example.com:3000 // 不同源,port 不同
學會分辨是否同源後,再回到 DOM 同源政策
和 Cookie 同源政策
來看看。
在瀏覽器中,圖片、影片、文字等資源在載入過後都會變成 DOM 元素,當我們試圖去載入其他網站的資源,會因為 DOM 同源政策而被阻止。
不過 DOM 同源政策
中,仍會有部分行為是被允許的,例如:
<form>
表單、連結、重新導向等。<img>
、<audio>
、<script>
等引入。寫入及嵌入在一般情況下是被允許的,但是 跨來源讀取
通常不被允許,例如使用 XMLHttpRequest
、Fetch
是會有問題的。也就是說當我們使用 JavaScript 進行跨來源的寫入、嵌入 、讀取的行為是有限制。
Cookie 同源政策
與 DOM 同源政策
有些許不同。Cookie 同源政策
只要滿足 domain 和 path 相同就視為同源,scheme 的部分則需要額外設定。
除此之外,子網域及母網域的 Cookie 是可以共用的。
上面的文章有提到:JavaScript 進行跨來源的寫入、嵌入 、讀取的行為是有限制。雖然能防止惡意攻擊,但總有些情況是例外,例如我們需要串接第三方 API 等。
在 CORS 的規範中,跨來源請求分為兩種:簡單請求
和 非簡單請求
。
簡單請求的條件有兩個:
HEAD
、GET
、POST
方法其中之一。不符合上述兩個條件都列為非簡單請求,下例為 MDN
中的非簡單請求範例(Header
中帶了自定義的 X-PINGOTHER: pingpong
):
var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/post-here/';
var body = '<?xml version="1.0"?><person><name>Arun</name></person>';
function callOtherDomain(){
if(invocation)
{
invocation.open('POST', url, true);
invocation.setRequestHeader('X-PINGOTHER', 'pingpong');
invocation.setRequestHeader('Content-Type', 'application/xml');
invocation.onreadystatechange = handler;
invocation.send(body);
}
}
一旦瀏覽器發現這是非簡單請求,就會自動發出預檢請求(Preflight Request),預檢請求成功後,本身的請求才會發出。
問題回到如何解決 CORS
的問題:方法是在 server 端的 Access-Control-Allow-Origin
新增自己的網址,或設定為 *
(全開),但全開的話還是有風險。
Access-Control-Allow-Origin: https://google.com
Access-Control-Allow-Origin: * // 全開
或是只開部分 HTTP 方法,client 端認證:
Access-Control-Request-Method: POST
Access-Control-Allow-Credentials: true